package com.black.play.entity;

import com.black.play.component.MineWindow;
import com.black.play.listener.MineKeyListener;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static com.black.play.entity.Grid.NODE_SIZE;

/**
 * 单元格类
 *
 * @author ylx
 */
public class Cell extends JButton
{
    // 横坐标
    private final int x;
    // 纵坐标
    private final int y;
    // 状态
    private CellStatus status;
    // 值
    private CellValue value;
    // 临时值
    private CellValue tempValue;
    // 类型是否是雷
    private boolean isMine;
    private final CellListener listener;
    // 上下左右
    public Cell up;
    public Cell down;
    public Cell left;
    public Cell right;
    public static final Font FONT = new Font("Arial", Font.BOLD, 20);

    public Cell(int x, int y)
    {
        this.x = x;
        this.y = y;
        setOpaque(false); // 设置为透明，以便我们自己绘制背景
        setContentAreaFilled(false); // 不填充内容区域，将在paintComponent中自定义
        setBorderPainted(false); // 不绘制默认边框
        setFocusPainted(false); // 不在获得焦点时绘制边框
        setPreferredSize(NODE_SIZE);
        // 鼠标事件监听
        this.listener = new CellListener();
    }

    /**
     * 单元格初始化
     * 非雷
     * 关闭
     * 监听状态初始化
     * 添加鼠标事件监听
     */
    public void init()
    {
        this.isMine = false;
        this.status = CellStatus.CLOSE;
        this.tempValue = CellValue.NULL;
        this.listener.lastStatus = this.status;
        addMouseListener(this.listener);
        // 允许键盘事件聚焦
        addKeyListener(MineKeyListener.getInstance());
        setFocusable(true);
        requestFocusInWindow();
    }

    public CellStatus getStatus()
    {
        return status;
    }

    public void setStatus(CellStatus status)
    {
        this.status = status;
    }

    public CellValue getValue()
    {
        return value;
    }

    public void setValue(CellValue value)
    {
        this.value = value;
    }

    public CellValue getTempValue()
    {
        return tempValue;
    }

    public void setTempValue(CellValue tempValue)
    {
        this.tempValue = tempValue;
    }

    public boolean isMine()
    {
        return isMine;
    }

    public void setMine(boolean mine)
    {
        isMine = mine;
    }

    public CellListener getListener()
    {
        return listener;
    }

    public void stopListener()
    {
        removeMouseListener(listener);
    }

    /**
     * 获取当前网格的邻居
     *
     * @return 邻居
     */
    public List<Cell> getNeighbors()
    {
        List<Cell> neighbors = new ArrayList<>();
        if (up != null)
        {
            neighbors.add(up);
            if (up.left != null)
            {
                neighbors.add(up.left);
            }
            if (up.right != null)
            {
                neighbors.add(up.right);
            }
        }
        if (down != null)
        {
            neighbors.add(down);
            if (down.left != null)
            {
                neighbors.add(down.left);
            }
            if (down.right != null)
            {
                neighbors.add(down.right);
            }
        }
        if (left != null)
        {
            neighbors.add(left);
        }
        if (right != null)
        {
            neighbors.add(right);
        }
        return neighbors;
    }

    @Override
    public int hashCode()
    {
        return Objects.hash(x, y);
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        Graphics2D graphics2D = (Graphics2D) g;
        // 绘制圆角边界
        RoundRectangle2D.Float border = new RoundRectangle2D.Float(0, 0, NODE_SIZE.width, NODE_SIZE.height, NODE_SIZE.width >> 2, NODE_SIZE.height >> 2);
        graphics2D.setColor(Color.WHITE);
        graphics2D.setStroke(new BasicStroke(3));
        graphics2D.draw(border);
        // 填充网格
        RoundRectangle2D.Float fill = new RoundRectangle2D.Float(0, 0, NODE_SIZE.width - 2, NODE_SIZE.height - 2, NODE_SIZE.width >> 2, NODE_SIZE.height >> 2);
        graphics2D.setColor(status.getBackground());
        graphics2D.fill(fill);
        // 点开或者右键标记时绘制文字
        if (status == CellStatus.OPEN || status == CellStatus.HALF_OPEN)
        {
            Color fontColor = tempValue == CellValue.NULL ? value.fontColor : tempValue.fontColor;
            String text = tempValue == CellValue.NULL ? value.text : tempValue.text;
            graphics2D.setColor(fontColor);
            graphics2D.setFont(FONT);
            FontMetrics fm = graphics2D.getFontMetrics();
            int x = (getWidth() - fm.stringWidth(text)) / 2;
            int y = (getHeight() - fm.getHeight()) / 2 + fm.getAscent();
            graphics2D.drawString(text, x, y);
        }
    }

    /**
     * 单元格监听器
     */
    public class CellListener extends MouseAdapter
    {
        private CellStatus lastStatus;

        public CellListener()
        {
            lastStatus = CellStatus.CLOSE;
        }

        /**
         * 鼠标点击事件
         *
         * @param e 鼠标事件
         */
        @Override
        public void mouseClicked(MouseEvent e)
        {
            // 如果已经打开，则不处理
            if (status == CellStatus.OPEN)
            {
                return;
            }
            // 获取点击的按钮
            int button = e.getButton();
            switch (button)
            {
                case MouseEvent.BUTTON1:
                    leftAction();
                    break;
                case MouseEvent.BUTTON3:
                    rightAction();
                    break;
                default:
                    break;
            }
        }

        /**
         * 鼠标移出事件 变更原来颜色
         * @param e 鼠标事件
         */
        @Override
        public void mouseExited(MouseEvent e)
        {
            if (status == CellStatus.OPEN)
            {
                return;
            }
            status = lastStatus;
            updateUI();
        }

        /**
         * 鼠标移入事件 变更hover颜色
         * @param e 鼠标事件
         */
        @Override
        public void mouseEntered(MouseEvent e)
        {
            if (status == CellStatus.OPEN)
            {
                return;
            }
            lastStatus = status;
            status = CellStatus.HOLD_ON;
            updateUI();
        }

        /**
         * 左键点击事件
         */
        public void leftAction()
        {
            // 开始计时(会自动判断是否已经开始计时)
            MineWindow instance = MineWindow.getInstance();
            instance.startTimer();
            // 如果已经打开或者标记，则不处理
            if (status == CellStatus.HALF_OPEN || status == CellStatus.OPEN)
            {
                return;
            }
            // 打开
            status = CellStatus.OPEN;
            // 如果是雷，则游戏结束
            if (value == CellValue.MINE)
            {
                instance.fail();
            }
            // 如果是0 则左键打开所有邻居
            else if (value == CellValue.NUMBER_0)
            {
                // getNeighbors().forEach(Cell::doClick);  依托按钮点击太卡,加载及其慢
                getNeighbors().stream().map(Cell::getListener).forEach(CellListener::leftAction);
            }
            updateUI();
            // 判断是否通过
            instance.judgeSuccess();
        }

        /**
         * 右键点击事件
         */
        private void rightAction()
        {
            MineWindow instance = MineWindow.getInstance();
            switch (tempValue)
            {
                case NULL:
                    // 无标记->旗子标记  判断通关
                    status = CellStatus.HALF_OPEN;
                    tempValue = CellValue.FLAG;
                    instance.subNum();
                    instance.judgeSuccess();
                    break;
                case FLAG:
                    // 旗子->问号标记
                    status = CellStatus.HALF_OPEN;
                    tempValue = CellValue.UNKNOWN;
                    instance.addNum();
                    break;
                case UNKNOWN:
                    // 问号->无标记
                    status = CellStatus.CLOSE;
                    tempValue = CellValue.NULL;
                    break;
            }
            lastStatus = status;
            updateUI();
        }
    }
}
